package com.excel.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import com.excel.tools.CellFormatter;
import com.excel.tools.Reflector;
import com.excel.vo.VO;

/**
 * 操作Excel文件的业务逻辑类.
 * <p/>
 * <b>Example</b>:<br/>
 * <p><i>1. 读取文件</i></p>
 * <code>
 * <pre>
 		ExcelService excelService = ExcelService.getService(); // 获取本类的实例
		
		File file = new File("E:/test.xls");
		excelService.setExcel(file); // 目标文件
		
		Map<String, String> columnMap = new HashMap<String, String>();
		columnMap.put("序号", "id");
		columnMap.put("姓名", "name");
		columnMap.put("日期", "date");
		columnMap.put("正误", "bool");
		columnMap.put("得分1", "point");
		columnMap.put("得分2", "point2");
		excelService.setColumnNameMap(columnMap); // 列名与属性名映射关系
		
		excelService.setSheetIndex(0); // 工作表号
		
		excelService.setDatePattern("yyyy-MM-dd HH:mm"); // 日期格式
		
		boolean b = excelService.read();         // 读取
		System.out.println("read方法执行结果：" + b);
		
		excelService.printFeedback();            // 打印反馈信息
		excelService.printList();                // 打印列表
 * </pre>
 * </code>
 * <p><i>2. 写入文件</i></p>
 * <code>
 * <pre>
		ExcelService excelService = ExcelService.getService(); // 获取本类的实例
		
		File file = new File("E:/test.xls");
		excelService.setExcel(file); // 目标文件
		
		Map<String, String> columnMap = new HashMap<String, String>();
		columnMap.put("序号", "id");
		columnMap.put("姓名", "name");
		columnMap.put("日期", "date");
		columnMap.put("正误", "bool");
		columnMap.put("得分1", "point");
		columnMap.put("得分2", "point2");
		excelService.setColumnNameMap(columnMap); // 列名与属性名映射关系
		
		List<VO> list = new ArrayList<VO>();
		VO vo = new VO();
		vo.setId(1);
		vo.setName("张三");
		vo.setDate(new Date());
		vo.setBool(true);
		vo.setPoint(56.4);
		vo.setPoint2(34.5f);
		list.add(vo);
		vo = new VO();
		vo.setId(2);
		vo.setName("李四");
		vo.setDate(new Date());
		vo.setBool(false);
		vo.setPoint(156.6);
		vo.setPoint2(134.5f);
		list.add(vo);
		vo = new VO();
		vo.setId(3);
		vo.setName("王五");
		vo.setDate(new Date());
		vo.setBool(false);
		vo.setPoint(256.8);
		vo.setPoint2(234.5f);
		list.add(vo);
		excelService.setList(list); // 待写入的数据列表
		
		boolean b = excelService.write(); // 写入
		System.out.println("write方法执行结果：" + b);
		
		excelService.printList();     // 打印列表
		excelService.printFeedback(); // 打印反馈信息
 * </pre>
 * </code>
 * 
 * @author Wang Kerui
 * 
 * @see com.excel.vo.VO
 * 
 */
public class ExcelService {
	
	private List<VO> list = null;                      // 文件记录的内存存储
	private File excel = null;                         // 操作目标，Excel文件
	private int sheetIndex = 0;                        // 工作表索引号，默认为第一个，0
	private Map<String, String> columnNameMap = null;  // VO类属性名与目标Excel文件列名映射，key是列名，value是VO类属性名
	private String primaryTitle = null;                // 标题
	private boolean hasPrimaryTitle = false;           // 是否附加标题，默认无标题
	private String datePattern = "yyyy-MM-dd HH:mm:ss";// 日期格式
	
	private String code = null;                  // 反馈操作结果代码，对外只读
	private Map<String, String> messages = null;  // 操作结果代码与信息映射，对外只读
	
	/**
	 * 私有构造方法.
	 * <p/>
	 * 默认的
	 * 
	 */
	private ExcelService () {
		initMessages();
	}
	
	/**
	 * 私有构造方法.
	 * 
	 * @param list 存储Excel记录的List对象
	 * @param excel 目标Excel文件
	 * @param sheetIndex 工作表索引号
	 * @param columnNameMap VO类属性名与文件列名的映射关系，key是列名，value是VO类属性名
	 * @param primaryTitle 主标题
	 * @param hasPrimaryTitle Excel文件是否具有主标题
	 */
	private ExcelService (List<VO> list, File excel, int sheetIndex, Map<String, String> columnNameMap, String primaryTitle, boolean hasPrimaryTitle) {
		this.list = list;
		this.excel = excel;
		this.sheetIndex = sheetIndex;
		this.columnNameMap = columnNameMap;
		if (hasPrimaryTitle) {
			this.primaryTitle = primaryTitle;
		}
		
		initMessages();
	}
	
	/**
	 * 获取本类的实例.
	 * <p/>
	 * 全部参数值为默认，即List对象和Excel文件对象均为null，读取文件的第一个工作表，属性名与列名映射关系暂无，文件无主标题
	 * 
	 * @return 该类的实例对象
	 */
	public static ExcelService getService() {
		return new ExcelService();
	}
	
	/**
	 * 获取本类的实例.
	 * <p/>
	 * 指定各个参数值
	 * 
	 * @param list 存储Excel记录的List对象
	 * @param excel 目标Excel文件
	 * @param sheetIndex 工作表索引号
	 * @param columnNameMap VO类属性名与文件列名的映射关系，key是列名，value是VO类属性名
	 * @param primaryTitle 主标题
	 * @param hasPrimaryTitle Excel文件是否具有主标题
	 * @return 该类的实例对象
	 */
	public static ExcelService getService(List<VO> list, File excel, int sheetIndex, Map<String, String> columnNameMap, String primaryTitle, boolean hasPrimaryTitle) {
		return new ExcelService(list, excel, sheetIndex, columnNameMap, primaryTitle, hasPrimaryTitle);
	}
	
	/**
	 * 读取Excel文件内容到list中.
	 * <p/>
	 * 读取时设置相关参数
	 * <br/>
	 * 重载方法
	 * 
	 * @param file 文件
	 * @param sheetIndex 工作表
	 * @return 如果文件读取无误，返回true；否则，返回false
	 */
	public boolean read(File file, int sheetIndex){
		this.excel = file;
		this.sheetIndex = sheetIndex;
		return this.read();
	}
	
	/**
	 * 读取Excel文件内容到list中.
	 * <p/>
	 * 读取时设置相关参数
	 * <br/>
	 * 重载方法
	 * 
	 * @param fileName 文件名称
	 * @param sheetIndex 工作表
	 * @return 如果文件读取无误，返回true；否则，返回false
	 */
	public boolean read(String fileName, int sheetIndex){
		this.excel = new File(fileName);
		this.sheetIndex = sheetIndex;
		return this.read();
	}

	/**
	 * 读取Excel文件内容到list中.
	 * 
	 * @return 如果文件读取无误，返回true；否则，返回false
	 */
	public boolean read() {
		// 检验参数是否合法
		if (excel == null) {
			code = "Error-001"; // excel参数不存在
			return false;
		}
		if (!excel.exists() || !excel.isFile()) {
			code = "Error-002"; // 文件不存在或该对象不是文件
			return false;
		}
		if (columnNameMap == null) {
			code = "Error-003"; // 属性、列名映射不存在
			return false;
		}
		String fileName = excel.getName(); // 文件名
		String ext = fileName.substring(fileName.lastIndexOf(".") + 1); // 文件扩展名
		if (!ext.equalsIgnoreCase("xls")) {
			code = "Error-004"; // 文件类型不允许
			return false;
		}
		
		// 初始化列表为空列表
		list = new ArrayList<VO>();

		FileInputStream fis = null;
		Workbook wb = null;
		Sheet sheet = null;
		Row row = null;
		try {
			fis = new FileInputStream(excel);
			
			// 工作簿
			wb = new HSSFWorkbook(new POIFSFileSystem(fis));
			//wb = new XSSFWorkbook();
			int sheetNum = wb.getNumberOfSheets(); // 工作表数目
			// 检查sheetIndex参数是否合法 
			if (sheetIndex >= sheetNum) {
				code = "Error-005"; // 工作表索引超出界限
				return false;
			}
			
			// 工作表
			sheet = wb.getSheetAt(sheetIndex);
			int rowNum = sheet.getLastRowNum(); // 总行数 ，将空行计算在内
			rowNum += 1;
			
			// 标题行
			int firstDataRowNum = 1;
			if (hasPrimaryTitle) {
				firstDataRowNum += 1;
			}
			Row titleRow = sheet.getRow(firstDataRowNum - 1);
			
			// 列名列表
			List<String> columnNames = new ArrayList<String>();
			if (titleRow != null) {
				Iterator<Cell> cellIterator = titleRow.cellIterator();
				while (cellIterator.hasNext()) {
					String value = CellFormatter.getStringCellValue(cellIterator.next());
					columnNames.add(value);
				}
			}
			
			// 数据行
			for (int i = firstDataRowNum; i < rowNum; i++) { // 正文内容
				row = sheet.getRow(i);
				if (row != null) {
					VO vo = new VO(); // 一条记录
					Iterator<Cell> cellIterator = row.cellIterator(); // 单元格迭代
					int j = 0;
					while (cellIterator.hasNext()) {
						Cell cell = cellIterator.next();
						Reflector.setter(vo, columnNameMap.get(columnNames.get(j)), cell, datePattern);
						j++;
					}
					list.add(vo);
				}
			}
			code = "OK-000"; // 文件成功读取完毕
			return true;
		} catch (FileNotFoundException e) {
			//System.out.println("文件不存在");
			code = "Error-002"; // 文件不存在
			e.printStackTrace();
		} catch (IOException e) {
			//System.out.println("文件IO异常");
			code = "Error-101";
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			code = "Error-006";
			e.printStackTrace();
		} catch (Exception e) {
			// System.out.println("文件读取过程中发生异常");
			code = "Error-101";
			e.printStackTrace();
		} finally {
			try {
				if (fis != null) {
					fis.close();
				}
			} catch (IOException e) {
				//System.out.println("文件流关闭异常");
				code = "Error-101";
				e.printStackTrace();
			}
		}
		return false;
	}
	
	/**
	 * 将list中的数据写入Excel文件中.
	 * 
	 * @return 如果文件写入无误，返回true；否则，返回false
	 */
	public boolean write(){
		// 检查参数是否合法
		if (list == null) {
			code = "Error-201"; // list参数不允许为null
			return false;
		}
		if (excel == null) {
			code = "Error-001"; // excel参数不允许为null
			return false;
		}
		String fileName = excel.getName(); // 文件名
		String ext = fileName.substring(fileName.lastIndexOf(".") + 1); // 文件扩展名
		if (!ext.equalsIgnoreCase("xls")) {
			code = "Error-004"; // 文件类型不允许
			return false;
		}
		if (!excel.exists()) { // 文件不存在
			if (!excel.getParentFile().exists()) { // 父路径不存在
				excel.getParentFile().mkdirs(); // 创建父文件夹
			}
			try {
				excel.createNewFile(); // 创建文件
			} catch (IOException e) {
				code = "Error-202"; // 文件创建失败
				e.printStackTrace();
				return false;
			}
		}
		if (columnNameMap == null) {
			code = "Error-003"; // 属性、列名映射不存在
			return false;
		}
		
		// 依次创建工作簿，工作表，单元格等
		HSSFWorkbook wb = new HSSFWorkbook(); // 工作簿
		Sheet sheet = wb.createSheet(); // 工作表
		Row row = null; // 行
		Cell cell = null; // 单元格
		int currRowNum = 0; // 当前行号
		
		// 写主标题
		if (hasPrimaryTitle) {
			row = sheet.createRow(currRowNum);
			cell = row.createCell(0);
			cell.setCellValue(primaryTitle);
			currRowNum ++; // 当前行下移
		}
		// 写列名行
		String[] columnNames = columnNameMap.keySet().toArray(new String[0]); // 列名数组
		row = sheet.createRow(currRowNum);
		for (int i = 0; i < columnNames.length; i++) {
			cell = row.createCell(i);
			cell.setCellValue(columnNames[i]);
		}
		currRowNum ++; // 当前行下移
		
		// 写数据行
		for (int i = 0; i < list.size(); i++) {
			VO vo = list.get(i);
			row = sheet.createRow(currRowNum);
			for (int j = 0; j < columnNames.length; j++) {
				String propertyName = columnNameMap.get(columnNames[j]);
				cell = row.createCell(j);
				try {
					Reflector.getter(vo, propertyName, cell, datePattern);
				} catch (NoSuchMethodException e) {
					code = "Error-006";
					e.printStackTrace();
				} catch (Exception e) {
					// System.out.println("文件写入过程中发生异常");
					code = "Error-102";
					e.printStackTrace();
				}
			}
			currRowNum ++; // 当前行下移
		}

		// 将创建好的工作簿写入文件
		try {
			FileOutputStream fos = new FileOutputStream(excel);
			wb.write(fos);
			fos.close();
			code = "OK-001";
			return true;
		} catch (FileNotFoundException e) {
			//System.out.println("文件不存在");
			code = "Error-002"; // 文件不存在
			e.printStackTrace();
		} catch (IOException e) {
			// System.out.println("文件写入过程中发生异常");
			code = "Error-102";
			e.printStackTrace();
		}
		
		return false;
	}
	
	/**
	 * 打印反馈信息.
	 * 
	 */
	public void printFeedback(){
		System.out.println(this.code + "：" + this.getMessage());
	}
	
	/**
	 * 打印List信息.
	 * 
	 */
	public void printList(){
		if (list != null) {
			for (int i = 0; i < list.size(); i++) {
				VO vo = list.get(i);
				System.out.println("[" + (i + 1) + "]" + vo);
			}
		}
	}
	
	/**
	 * 初始化结果代码映射.
	 * 
	 */
	private void initMessages(){
		messages = new HashMap<String, String>();
		messages.put("Error-001", "参数excel不允许为null");
		messages.put("Error-002", "文件不存在");
		messages.put("Error-003", "列名映射不存在");
		messages.put("Error-004", "文件类型不允许，只能操作.xls文件");
		messages.put("Error-005", "工作表索引超出限制");
		messages.put("Error-006", "列名映射关系有误");
		
		messages.put("Error-201", "参数list不允许为null");
		messages.put("Error-202", "文件创建失败");
		
		messages.put("Error-101", "文件读取过程中发生异常");
		messages.put("Error-102", "文件写入过程中发生异常");
		
		messages.put("OK-000", "文件读取完毕");
		messages.put("OK-001", "文件写入完毕");
	}
	

	// /**
	// * 通过map的值获取键值.
	// *
	// * @param map
	// * @param value
	// * @return
	// */
	// private String getKey (Map<String, String> map, String value) {
	// Iterator<String> keyIterator = columnNameMap.keySet().iterator();
	// while (keyIterator.hasNext()) {
	// String key = (String) keyIterator.next();
	// if (value != null && value.equals(map.get(key))) {
	// return key;
	// }
	// }
	// return null;
	// }

	public List<VO> getList() {
		return list;
	}

	public void setList(List<VO> list) {
		this.list = list;
	}

	public File getExcel() {
		return excel;
	}

	public void setExcel(File excel) {
		this.excel = excel;
	}

	public int getSheetIndex() {
		return sheetIndex;
	}

	public void setSheetIndex(int sheetIndex) {
		this.sheetIndex = sheetIndex;
	}

	public Map<String, String> getColumnNameMap() {
		return columnNameMap;
	}

	public void setColumnNameMap(Map<String, String> columnNameMap) {
		this.columnNameMap = columnNameMap;
	}

	public String getPrimaryTitle() {
		return primaryTitle;
	}

	public void setPrimaryTitle(String primaryTitle) {
		this.primaryTitle = primaryTitle;
	}

	public boolean hasPrimaryTitle() {
		return hasPrimaryTitle;
	}

	public void hasPrimaryTitle(boolean hasPrimaryTitle) {
		this.hasPrimaryTitle = hasPrimaryTitle;
	}

	public String getMessage() {
		return messages.get(code);
	}

	public String getDatePattern() {
		return datePattern;
	}

	public void setDatePattern(String datePattern) {
		this.datePattern = datePattern;
	}
}
